package rwt.server.core;

import java.util.*;

import rwt.server.RailroadMessages.*;
import rwt.server.WebMessages.*;
import rwt.server.handlers.*;
import rwt.server.handlers.web.*;

/**
 * An enumeration of message types.
 */
public enum MessageType
{
	// Extended railroad messages (ID range: 1 - 127)
	// - Higher-level messages for the Ada Controller program
	
	EX_PUT_TRAIN_STATE(1, PutTrainStateMsg.class, new PutTrainStateHandler()),
	EX_PUT_SWITCH_STATE(4, PutSwitchStateMsg.class, new PutSwitchStateHandler()),
	
	EX_DO_LOCO_INIT(8, DoLocoInitMsg.class),
	EX_PUT_INIT_OUTCOME(9, PutInitOutcomeMsg.class, new PutInitOutcomeHandler()),
	
	EX_DO_READ_LAYOUT(10, DoReadLayoutMsg.class),
	EX_PUT_READ_LAYOUT(11, PutReadLayoutResponseMsg.class, new PutReadLayoutResponseHandler()),
	
	EX_PUT_TRAIN_INFORMATION(21, PutTrainInformationMsg.class, new PutTrainInformationHandler()),
	
	EX_GET_SWITCH_STATES(22, GetSwitchStatesMsg.class),
	
	// LocoNet messages (ID range: 128 - 255)
	// - Low-level messages for the railroad hardware (also understood by Controller)

	POWER_OFF_MSG(0x82, PowerOffMsg.class),
	POWER_ON_MSG(0x83, PowerOnMsg.class),
	LOCO_SPD_MSG(0xA0, LocoSpdMsg.class, new LocoSpdHandler()),
	LOCO_DIRF_MSG(0xA1, LocoDirfMsg.class, new LocoDirfHandler()),
	LOCO_SND_MSG(0xA2, LocoSndMsg.class),
	SW_REQ_MSG(0xB0, SwReqMsg.class),
	SW_REP_MSG(0xB1, SwRepMsg.class, new SwRepHandler()),
	MOVE_SLOTS_MSG(0xBA, MoveSlotsMsg.class, new MoveSlotsHandler()),
	LOCO_ADR_MSG(0xBF, LocoAdrMsg.class, new LocoAdrHandler()),
	SL_RD_DATA_MSG(0xE7, SlRdDataMsg.class, new SlRdDataHandler()),
	WR_SL_DATA_MSG(0xEF, WriteSlotDataToClearMsg.class),
	
	// Web messages (ID range: 1000+)
	// - Messages to and from web clients (passed in JSON format over a WebSocket connection)
	
	WEB_CHANGE_SYSTEM_STATUS(1000, ChangeSystemStatus.class, new ChangeSystemStatusHandler()),
	WEB_PUT_SYSTEM_STATUS(1001, PutSystemStatus.class),
	WEB_DO_INIT_TRAIN(1002, DoInitializeTrain.class, new DoInitializeTrainHandler()),
	WEB_PUT_INIT_TRAIN(1003, PutInitializeTrain.class),
	WEB_CHANGE_POWER_STATUS(1004, ChangePowerStatus.class, new ChangePowerStatusHandler()),
	WEB_OPEN_THROTTLE_REQUEST(1050, OpenThrottleRequest.class, new OpenThrottleRequestHandler()),
	WEB_OPEN_THROTTLE_RESPONSE(1051, OpenThrottleResponse.class),
	WEB_CLOSE_THROTTLE(1052, CloseThrottle.class, new CloseThrottleHandler()),
	WEB_PUT_AVAILABLE_TRAINS(1099, PutAvailableTrains.class),
	WEB_SET_TRAIN_SPEED(1100, SetTrainSpeed.class, new SetTrainSpeedHandler()),
	WEB_SET_TRAIN_DIRECTION(1102, SetTrainDirection.class, new SetTrainDirectionHandler()),
	WEB_SET_TRAIN_LIGHT(1104, SetTrainLight.class, new SetTrainLightHandler()),
	WEB_SET_TRAIN_HORN(1106, SetTrainHorn.class, new SetTrainHornHandler()),
	WEB_SET_TRAIN_BELL(1108, SetTrainBell.class, new SetTrainBellHandler()),
	WEB_SET_TRAIN_MUTE(1110, SetTrainMute.class, new SetTrainMuteHandler()),
	WEB_SET_TRAIN_NEXT_SWITCH(1112, SetTrainNextSwitch.class, new SetTrainNextSwitchHandler()),
	WEB_PUT_TRAIN_INFO(1151, PutTrainInfo.class),
	WEB_PUT_TRAIN_STATE(1153, PutTrainState.class),
	WEB_SET_SWITCH_STATE(1200, SetSwitch.class, new SetSwitchHandler()),
	WEB_PUT_SWITCH_STATE(1201, PutSwitchState.class),
	WEB_GET_SWITCHES(1202, GetSwitches.class, new GetSwitchesHandler()),
	WEB_PUT_SWITCHES(1203, PutSwitches.class),
	WEB_OPEN_SWITCH_REQUEST(1210, OpenSwitchesRequest.class, new OpenSwitchRequestHandler()),
	WEB_OPEN_SWITCH_RESPONSE(1211, OpenSwitchesResponse.class),
	WEB_CLOSE_SWITCH_REQUEST(1212, CloseSwitchRequest.class, new CloseSwitchHandler()),
	WEB_CLOSE_SWITCHES(1213, CloseSwitches.class),
	WEB_WELCOME_MESSAGE(1999, WelcomeMessage.class)
	;
	
	private static final Map<Integer, MessageType> idLookup = new HashMap<>();
	
	// Static initialization block: build lookup table.
	static
	{
		for (MessageType type : MessageType.values())
			idLookup.put(type.getId(), type);
	}
	
	public static MessageType fromId(int id)
	{
		return idLookup.get(id);
	}
	
	private int id;
	private Class<? extends BaseMessage> messageClass;
	private MessageHandler<BaseMessage> handler;
	
	private MessageType(int id, Class<? extends BaseMessage> messageClass)
	{
		this(id, messageClass, null);
	}
	
	@SuppressWarnings("unchecked")
	private <T extends BaseMessage> MessageType(int id, Class<T> messageClass, MessageHandler<T> handler)
	{
		this.id = id;
		this.messageClass = messageClass;
		this.handler = (MessageHandler<BaseMessage>) handler;
	}
	
	public int getId()
	{
		return id;
	}
	
	public MessageHandler<BaseMessage> getHandler()
	{
		return handler;
	}
	
	public Class<? extends BaseMessage> getMessageClass()
	{
		return messageClass;
	}
	
	public static Map<Integer, Class<? extends BaseMessage>> getClassMap()
	{
		Map<Integer, Class<? extends BaseMessage>> classMap = new HashMap<>();
		for (MessageType type : MessageType.values())
		{
			if (classMap.containsKey(type.getId()))
				throw new RuntimeException("MessageType defines multiple messages with ID: " + type.getId());
			classMap.put(type.getId(), type.getMessageClass());
		}
		
		return classMap;
	}
}
